Skip to content

feat: Add ORD Integration Dependency support via CDS annotations#48

Open
f-nie wants to merge 3 commits intomainfrom
feat/ord3
Open

feat: Add ORD Integration Dependency support via CDS annotations#48
f-nie wants to merge 3 commits intomainfrom
feat/ord3

Conversation

@f-nie
Copy link
Contributor

@f-nie f-nie commented Mar 18, 2026

feat: Add ORD Integration Dependency support via messaging.subscribe()

Summary

This PR adds support for generating ORD Integration Dependencies for consumed events using the messaging.subscribe() method with an eventResourceOrdId option.

When both @cap-js/event-broker and @cap-js/ord plugins are installed, consumed events are automatically documented in the application's ORD metadata.

Motivation

Applications using Event Broker to consume external CloudEvents (e.g., from S/4HANA) should declare these dependencies in their ORD document for:

  • Discoverability: External tools can see which events an application depends on
  • Governance: Complete dependency graph for event-driven integrations
  • Alignment: Matches Java CAP plugin (cds-feature-event-hub) behavior

Changes

Modified Files

File Description
cds-plugin.js Added subscribe() method with eventResourceOrdId option support
README.md Added ORD Integration documentation

Usage

Subscribe to events with ORD metadata

Use messaging.subscribe() with the eventResourceOrdId option to both subscribe to events and declare them in ORD:

// srv/server.js
const cds = require("@sap/cds");

cds.once("served", async () => {
  const messaging = await cds.connect.to("messaging");

  // Subscribe to S/4HANA SalesOrder events
  messaging.subscribe(
    "sap.s4.beh.salesorder.v1.SalesOrder.Changed.v1",
    {
      eventResourceOrdId: "sap.s4:eventResource:CE_SALESORDEREVENTS:v1",
    },
    async (msg) => {
      console.log("Received SalesOrder.Changed event:", msg.data);
    },
  );

  // Subscribe to BusinessPartner events
  messaging.subscribe(
    "sap.s4.beh.businesspartner.v1.BusinessPartner.Created.v1",
    {
      eventResourceOrdId: "sap.s4:eventResource:CE_BUSINESSPARTNEREVENTS:v1",
    },
    async (msg) => {
      console.log("Received BusinessPartner.Created event:", msg.data);
    },
  );
});

Result: ORD Integration Dependency

At runtime, the ORD document at /.well-known/open-resource-discovery includes:

{
  "integrationDependencies": [
    {
      "ordId": "customer.myapp:integrationDependency:consumedEvents:v1",
      "title": "Consumed Events",
      "aspects": [
        {
          "title": "Subscribed Event Types",
          "eventResources": [
            {
              "ordId": "sap.s4:eventResource:CE_SALESORDEREVENTS:v1",
              "subset": [
                {
                  "eventType": "sap.s4.beh.salesorder.v1.SalesOrder.Changed.v1"
                }
              ]
            },
            {
              "ordId": "sap.s4:eventResource:CE_BUSINESSPARTNEREVENTS:v1",
              "subset": [
                {
                  "eventType": "sap.s4.beh.businesspartner.v1.BusinessPartner.Created.v1"
                }
              ]
            }
          ]
        }
      ]
    }
  ]
}

Multiple Event Types per Event Resource

When multiple events belong to the same event resource, they are automatically grouped:

// Both SalesOrder events share the same event resource
messaging.subscribe(
  "sap.s4.beh.salesorder.v1.SalesOrder.Changed.v1",
  { eventResourceOrdId: "sap.s4:eventResource:CE_SALESORDEREVENTS:v1" },
  handler,
);
messaging.subscribe(
  "sap.s4.beh.salesorder.v1.SalesOrder.Created.v1",
  { eventResourceOrdId: "sap.s4:eventResource:CE_SALESORDEREVENTS:v1" },
  handler,
);

Results in:

{
  "ordId": "sap.s4:eventResource:CE_SALESORDEREVENTS:v1",
  "subset": [
    { "eventType": "sap.s4.beh.salesorder.v1.SalesOrder.Changed.v1" },
    { "eventType": "sap.s4.beh.salesorder.v1.SalesOrder.Created.v1" }
  ]
}

How It Works

  1. messaging.subscribe() calls: The plugin intercepts subscribe() calls and stores mappings of event types to Event Resource ORD IDs
  2. At startup (cds.once('served')): Plugin registers a provider with the ORD plugin's Extension Registry
  3. Provider callback: Returns the collected event resources with their event types
  4. ORD plugin: Builds the Integration Dependency structure from provider data

subscribe() Method Signature

messaging.subscribe(eventType, options, handler);
Parameter Type Description
eventType string The fully qualified event type name
options object Options including eventResourceOrdId
handler function Event handler callback

The eventResourceOrdId option specifies the ORD ID of the external Event Resource that contains this event type.

Design Decisions

Why messaging.subscribe() instead of CDS annotations?

Approach Pros Cons
CDS annotations CAP-idiomatic, visible in model Requires separate CDS + code, easy to drift
messaging.subscribe() (this) Single location, directly tied to handler Only runtime, not build-time

Using subscribe() consolidates event subscription and ORD declaration in one place. This ensures the ORD metadata always matches what the application actually subscribes to.

Runtime-Only Support

Integration Dependencies are only generated at runtime, matching the Java plugin behavior. Build-time would require parsing service implementation code.

Comparison with Java Plugin

Feature Java (cds-feature-event-hub) Node.js (@cap-js/event-broker)
Runtime Support SPI-based (OrdIntegrationDependencyProvider) Extension Registry
Configuration No config, reads from subscriptions eventResourceOrdId in subscribe() call
Event Detection Spring bean introspection subscribe() method interception
Multiple Services Aggregated Aggregated

Checklist

  • README.md updated with new subscribe-based approach
  • subscribe() method added to messaging service
  • Event resources grouped by ordId
  • Plugin loads successfully

References

Related PR for ORD plugin to add the extension registry: cap-js/ord#399

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant